﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.OleDb;
using System.Data.Common;

namespace DbSharp.Framework.Driver.FieldPersistence
{
    class ExcelOleFieldPersistence : IFieldPersistence
    {
        #region IFieldPersistence<OleDbType> Members

        public List<int> ListSupportedScalarDataType
        {
            get
            {
                var list = new List<int>();
                list.Add((int)OleDbType.VarChar);
                list.Add((int)OleDbType.VarWChar);
                list.Add((int)OleDbType.Date);
                list.Add((int)OleDbType.DBTimeStamp);
                list.Add((int)OleDbType.Decimal);
                list.Add((int)OleDbType.Currency);
                list.Add((int)OleDbType.Single);
                list.Add((int)OleDbType.Double);
                list.Add((int)OleDbType.Integer);

                return list;
            }
        }

        public List<int> ListSupportedRecordsetDataType
        {
            get
            {
                var list = new List<int>();
                return list;
            }
        }

        public string ConvertToString(object value, int valueType)
        {
            OleDbType valueType1 = (OleDbType)valueType;
            var exceptionFmt = "{0} type is not supported.";
            var exceptionMsg = string.Format(exceptionFmt, valueType1.ToString());
            if (!ListSupportedScalarDataType.Contains(valueType))
            {
                throw new NotSupportedException(exceptionMsg);
            }

            string result;
            switch (valueType1)
            {
                case OleDbType.VarChar:
                case OleDbType.VarWChar:
                case OleDbType.Date:
                case OleDbType.DBTimeStamp:
                case OleDbType.Decimal:
                case OleDbType.Currency:
                case OleDbType.Single:
                case OleDbType.Double:
                case OleDbType.Integer:
                    result = FieldPersistenceHelper.SafeConvertToString(value);
                    break;
                default:
                    throw new NotSupportedException(exceptionMsg);
            }
            return result;
        }

        public decimal RestoreStringToDecimal(string valueStr, int valueType)
        {
            var valueType1 = (OleDbType)valueType;
            decimal result;
            switch (valueType1)
            {
                case OleDbType.Decimal:
                case OleDbType.Currency:
                case OleDbType.Single:
                case OleDbType.Double:
                case OleDbType.Integer:
                    result = Convert.ToDecimal(valueStr);
                    break;
                default:
                    throw new NotSupportedException(string.Format("{0} type is not supported.", valueType1.ToString()));
            }
            return result;
        }

        public Devart.Data.Oracle.OracleNumber RestoreStringToLargeDecimal(string valueStr, int valueType)
        {
            var valueType1 = (OleDbType)valueType;
            Devart.Data.Oracle.OracleNumber result;
            switch (valueType1)
            {
                case OleDbType.Decimal:
                case OleDbType.Currency:
                case OleDbType.Single:
                case OleDbType.Double:
                case OleDbType.Integer:
                    result = Devart.Data.Oracle.OracleNumber.Parse(valueStr);
                    break;
                default:
                    throw new NotSupportedException(string.Format("{0} type is not supported.", valueType1.ToString()));
            }
            return result;
        }





        public int GetSpecificDbType(DbDataReader dataReader, int columnIndex)
        {
            //class: System.Data.OleDb.OleDbDataReader
            //method: public override string GetDataTypeName(int index)

            //var method = dataReader.GetType().GetMethod("GetDataTypeName");
            //var parameters = new object[] { columnIndex };
            //var typeName = (string)method.Invoke(dataReader, parameters);

            var reader = (System.Data.OleDb.OleDbDataReader)dataReader;
            var typeName = reader.GetDataTypeName(columnIndex);

            //The following constant values is taken in class of System.Data.OleDb.NativeDBType
            /*
              private static readonly NativeDBType D_BigInt = new NativeDBType(0x13, 8, true, false, OleDbType.BigInt, 20, "DBTYPE_I8", typeof(long), 20, DbType.Int64);
              private static readonly NativeDBType D_Binary = new NativeDBType(0xff, -1, true, false, OleDbType.Binary, 0x80, "DBTYPE_BINARY", typeof(byte[]), 0x80, DbType.Binary);
              private static readonly NativeDBType D_Boolean = new NativeDBType(0xff, 2, true, false, OleDbType.Boolean, 11, "DBTYPE_BOOL", typeof(bool), 11, DbType.Boolean);
              private static readonly NativeDBType D_BSTR = new NativeDBType(0xff, ADP.PtrSize, false, false, OleDbType.BSTR, 8, "DBTYPE_BSTR", typeof(string), 8, DbType.String);
              private static readonly NativeDBType D_Chapter = new NativeDBType(0xff, ADP.PtrSize, false, false, OleDbType.Empty, 0x88, "DBTYPE_UDT", typeof(IDataReader), 0x88, DbType.Object);
              private static readonly NativeDBType D_Char = new NativeDBType(0xff, -1, true, false, OleDbType.Char, 0x81, "DBTYPE_CHAR", typeof(string), 130, DbType.AnsiStringFixedLength);
              private static readonly NativeDBType D_Currency = new NativeDBType(0x13, 8, true, false, OleDbType.Currency, 6, "DBTYPE_CY", typeof(decimal), 6, DbType.Currency);
              private static readonly NativeDBType D_Date = new NativeDBType(0xff, 8, true, false, OleDbType.Date, 7, "DBTYPE_DATE", typeof(DateTime), 7, DbType.DateTime);
              private static readonly NativeDBType D_DBDate = new NativeDBType(0xff, 6, true, false, OleDbType.DBDate, 0x85, "DBTYPE_DBDATE", typeof(DateTime), 0x85, DbType.Date);
              private static readonly NativeDBType D_DBTime = new NativeDBType(0xff, 6, true, false, OleDbType.DBTime, 0x86, "DBTYPE_DBTIME", typeof(TimeSpan), 0x86, DbType.Time);
              private static readonly NativeDBType D_DBTimeStamp = new NativeDBType(0xff, 0x10, true, false, OleDbType.DBTimeStamp, 0x87, "DBTYPE_DBTIMESTAMP", typeof(DateTime), 0x87, DbType.DateTime);
              private static readonly NativeDBType D_Decimal = new NativeDBType(0x1c, 0x10, true, false, OleDbType.Decimal, 14, "DBTYPE_DECIMAL", typeof(decimal), 14, DbType.Decimal);
              private static readonly NativeDBType D_Double = new NativeDBType(15, 8, true, false, OleDbType.Double, 5, "DBTYPE_R8", typeof(double), 5, DbType.Double);
              private static readonly NativeDBType D_Empty = new NativeDBType(0xff, 0, false, false, OleDbType.Empty, 0, "", null, 0, DbType.Object);
              private static readonly NativeDBType D_Error = new NativeDBType(0xff, 4, true, false, OleDbType.Error, 10, "DBTYPE_ERROR", typeof(int), 10, DbType.Int32);
              private static readonly NativeDBType D_Filetime = new NativeDBType(0xff, 8, true, false, OleDbType.Filetime, 0x40, "DBTYPE_FILETIME", typeof(DateTime), 0x40, DbType.DateTime);
              private static readonly NativeDBType D_Guid = new NativeDBType(0xff, 0x10, true, false, OleDbType.Guid, 0x48, "DBTYPE_GUID", typeof(Guid), 0x48, DbType.Guid);
              private static readonly NativeDBType D_IDispatch = new NativeDBType(0xff, ADP.PtrSize, true, false, OleDbType.IDispatch, 9, "DBTYPE_IDISPATCH", typeof(object), 9, DbType.Object);
              private static readonly NativeDBType D_Integer = new NativeDBType(10, 4, true, false, OleDbType.Integer, 3, "DBTYPE_I4", typeof(int), 3, DbType.Int32);
              private static readonly NativeDBType D_IUnknown = new NativeDBType(0xff, ADP.PtrSize, true, false, OleDbType.IUnknown, 13, "DBTYPE_IUNKNOWN", typeof(object), 13, DbType.Object);
              private static readonly NativeDBType D_LongVarBinary = new NativeDBType(0xff, -1, false, true, OleDbType.LongVarBinary, 0x80, "DBTYPE_LONGVARBINARY", typeof(byte[]), 0x80, DbType.Binary);
              private static readonly NativeDBType D_LongVarChar = new NativeDBType(0xff, -1, false, true, OleDbType.LongVarChar, 0x81, "DBTYPE_LONGVARCHAR", typeof(string), 130, DbType.AnsiString);
              private static readonly NativeDBType D_LongVarWChar = new NativeDBType(0xff, -1, false, true, OleDbType.LongVarWChar, 130, "DBTYPE_WLONGVARCHAR", typeof(string), 130, DbType.String);
              private static readonly NativeDBType D_Numeric = new NativeDBType(0x1c, 0x13, true, false, OleDbType.Numeric, 0x83, "DBTYPE_NUMERIC", typeof(decimal), 0x83, DbType.Decimal);
              private static readonly NativeDBType D_PropVariant = new NativeDBType(0xff, NativeOledbWrapper.SizeOfPROPVARIANT, true, false, OleDbType.PropVariant, 0x8a, "DBTYPE_PROPVARIANT", typeof(object), 12, DbType.Object);
              private static readonly NativeDBType D_Single = new NativeDBType(7, 4, true, false, OleDbType.Single, 4, "DBTYPE_R4", typeof(float), 4, DbType.Single);
              private static readonly NativeDBType D_SmallInt = new NativeDBType(5, 2, true, false, OleDbType.SmallInt, 2, "DBTYPE_I2", typeof(short), 2, DbType.Int16);
              private static readonly NativeDBType D_TinyInt = new NativeDBType(3, 1, true, false, OleDbType.TinyInt, 0x10, "DBTYPE_I1", typeof(short), 0x10, DbType.SByte);
              private static readonly NativeDBType D_Udt = new NativeDBType(0xff, -1, false, false, OleDbType.VarBinary, 0x84, "DBTYPE_BINARY", typeof(byte[]), 0x80, DbType.Binary);
              private static readonly NativeDBType D_UnsignedBigInt = new NativeDBType(20, 8, true, false, OleDbType.UnsignedBigInt, 0x15, "DBTYPE_UI8", typeof(decimal), 0x15, DbType.UInt64);
              private static readonly NativeDBType D_UnsignedInt = new NativeDBType(10, 4, true, false, OleDbType.UnsignedInt, 0x13, "DBTYPE_UI4", typeof(long), 0x13, DbType.UInt32);
              private static readonly NativeDBType D_UnsignedSmallInt = new NativeDBType(5, 2, true, false, OleDbType.UnsignedSmallInt, 0x12, "DBTYPE_UI2", typeof(int), 0x12, DbType.UInt16);
              private static readonly NativeDBType D_UnsignedTinyInt = new NativeDBType(3, 1, true, false, OleDbType.UnsignedTinyInt, 0x11, "DBTYPE_UI1", typeof(byte), 0x11, DbType.Byte);
              private static readonly NativeDBType D_VarBinary = new NativeDBType(0xff, -1, false, false, OleDbType.VarBinary, 0x80, "DBTYPE_VARBINARY", typeof(byte[]), 0x80, DbType.Binary);
              private static readonly NativeDBType D_VarChar = new NativeDBType(0xff, -1, false, false, OleDbType.VarChar, 0x81, "DBTYPE_VARCHAR", typeof(string), 130, DbType.AnsiString);
              private static readonly NativeDBType D_Variant = new NativeDBType(0xff, ODB.SizeOf_Variant, true, false, OleDbType.Variant, 12, "DBTYPE_VARIANT", typeof(object), 12, DbType.Object);
              private static readonly NativeDBType D_VarNumeric = new NativeDBType(0xff, 0x10, true, false, OleDbType.VarNumeric, 0x8b, "DBTYPE_VARNUMERIC", typeof(decimal), 14, DbType.VarNumeric);
              private static readonly NativeDBType D_VarWChar = new NativeDBType(0xff, -1, false, false, OleDbType.VarWChar, 130, "DBTYPE_WVARCHAR", typeof(string), 130, DbType.String);
              private static readonly NativeDBType D_WChar = new NativeDBType(0xff, -1, true, false, OleDbType.WChar, 130, "DBTYPE_WCHAR", typeof(string), 130, DbType.StringFixedLength);
              private static readonly NativeDBType D_Xml = new NativeDBType(0xff, -1, false, false, OleDbType.VarWChar, 0x8d, "DBTYPE_XML", typeof(string), 130, DbType.String);
           */

            var metaTypeMapping = new Dictionary<string, OleDbType>();
            metaTypeMapping.Add("DBTYPE_I8", OleDbType.BigInt);
            metaTypeMapping.Add("DBTYPE_BINARY", OleDbType.Binary);
            metaTypeMapping.Add("DBTYPE_BOOL", OleDbType.Boolean);
            metaTypeMapping.Add("DBTYPE_BSTR", OleDbType.BSTR);
            metaTypeMapping.Add("DBTYPE_UDT", OleDbType.Empty);
            metaTypeMapping.Add("DBTYPE_CHAR", OleDbType.Char);
            metaTypeMapping.Add("DBTYPE_CY", OleDbType.Currency);
            metaTypeMapping.Add("DBTYPE_DATE", OleDbType.Date);
            metaTypeMapping.Add("DBTYPE_DBDATE", OleDbType.DBDate);
            metaTypeMapping.Add("DBTYPE_DBTIME", OleDbType.DBTime);
            metaTypeMapping.Add("DBTYPE_DBTIMESTAMP", OleDbType.DBTimeStamp);
            metaTypeMapping.Add("DBTYPE_DECIMAL", OleDbType.Decimal);
            metaTypeMapping.Add("DBTYPE_R8", OleDbType.Double);
            metaTypeMapping.Add("", OleDbType.Empty);
            metaTypeMapping.Add("DBTYPE_ERROR", OleDbType.Error);
            metaTypeMapping.Add("DBTYPE_FILETIME", OleDbType.Filetime);
            metaTypeMapping.Add("DBTYPE_GUID", OleDbType.Guid);
            metaTypeMapping.Add("DBTYPE_IDISPATCH", OleDbType.IDispatch);
            metaTypeMapping.Add("DBTYPE_I4", OleDbType.Integer);
            metaTypeMapping.Add("DBTYPE_IUNKNOWN", OleDbType.IUnknown);
            metaTypeMapping.Add("DBTYPE_LONGVARBINARY", OleDbType.LongVarBinary);
            metaTypeMapping.Add("DBTYPE_LONGVARCHAR", OleDbType.LongVarChar);
            metaTypeMapping.Add("DBTYPE_WLONGVARCHAR", OleDbType.LongVarWChar);
            metaTypeMapping.Add("DBTYPE_NUMERIC", OleDbType.Numeric);
            metaTypeMapping.Add("DBTYPE_PROPVARIANT", OleDbType.PropVariant);
            metaTypeMapping.Add("DBTYPE_R4", OleDbType.Single);
            metaTypeMapping.Add("DBTYPE_I2", OleDbType.SmallInt);
            metaTypeMapping.Add("DBTYPE_I1", OleDbType.TinyInt);
            //metaTypeMapping.Add("DBTYPE_BINARY", OleDbType.VarBinary);
            metaTypeMapping.Add("DBTYPE_UI8", OleDbType.UnsignedBigInt);
            metaTypeMapping.Add("DBTYPE_UI4", OleDbType.UnsignedInt);
            metaTypeMapping.Add("DBTYPE_UI2", OleDbType.UnsignedSmallInt);
            metaTypeMapping.Add("DBTYPE_UI1", OleDbType.UnsignedTinyInt);
            metaTypeMapping.Add("DBTYPE_VARBINARY", OleDbType.VarBinary);
            metaTypeMapping.Add("DBTYPE_VARCHAR", OleDbType.VarChar);
            metaTypeMapping.Add("DBTYPE_VARIANT", OleDbType.Variant);
            metaTypeMapping.Add("DBTYPE_VARNUMERIC", OleDbType.VarNumeric);
            metaTypeMapping.Add("DBTYPE_WVARCHAR", OleDbType.VarWChar);
            metaTypeMapping.Add("DBTYPE_WCHAR", OleDbType.WChar);
            metaTypeMapping.Add("DBTYPE_XML", OleDbType.VarWChar);


            OleDbType result;
            var found = metaTypeMapping.TryGetValue(typeName, out result);
            if (found)
                return (int)result;
            else
                return -1;
        }
 

        public int GetSpecificDbType(DbParameter dbParam)
        {
            var typ = ((OleDbParameter)dbParam).OleDbType;
            return (int)typ;
        }


        public int NumberTypeWhenParseLiteral
        {
            get { return (int)(OleDbType.Decimal); }
        }


        public bool IsStringType(int valueType)
        {
            bool result = false;
            var valueType1 = (OleDbType)valueType;
            switch (valueType1)
            {
                case OleDbType.VarChar:
                case OleDbType.VarWChar: 
                    result = true;
                    break;
                default:
                    result = false;
                    break;
            }

            return result;
        }

        #endregion
    }
}